home *** CD-ROM | disk | FTP | other *** search
Wrap
var EXPORTED_SYMBOLS = ["YOONO_STORAGE"]; // Globals const CI = Components.interfaces; const CL = Components.classes; ///// const YOONO_DIRSERVICE = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties); const CONSOLESERVICE = CL["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService); const PERMS_FILE = 0655; ///// var yoono = {}; const YOONO_DB_REQUIRED_VERSION = 1; function YoonoStorage() { this.dataBase = null; this.mainThread = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread; this.backgroundThread = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0); this.dbFileName = ""; } YoonoStorage.prototype.init = function (y) { yoono = y; } /** * * @param aStatement string for the statement, with placeholders ?1, ?2, etc * @param aValues array of arrays , each holding the values to bind to the placeholders for one execution of the statement */ YoonoStorage.prototype.executeSql = function(aStatement, aDataSet) { // Init the database the first time it's needed var statusObject = {}; var statement = null; statusObject.errorString = ""; statusObject.errorCode = 0; if(!this.dataBase) { this.openStorage(); // is that a good idea ? } if(!this.dataBase) { statusObject.errorString = "Unable to open the database file"; statusObject.errorCode = 14; yoono.log.error("Unable to execute : "+aStatement+"\nbecause DB is not open!"); return [statusObject]; } try { statement = this.dataBase.createStatement(aStatement); } catch(e) { try { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; } catch(e1) { statusObject.errorString = e1.message || e1; statusObject.errorCode = -1; } yoono.log.error("Bad SQL query : "+aStatement+"\nbecause of : "+statusObject.errorString+" (" + statusObject.errorCode + ")\n"); return [statusObject]; } if(!aDataSet) aDataSet = [[]]; // all statements don't need values... But they need to be executed at least once... var nbDataSet = aDataSet.length; // always at least 1 // The VACUUM statement can't be performed from within a transaction if('vacuum' != aStatement) { try { this.dataBase.beginTransaction(); // will fail if a transaction is already running } catch(e) { if(0 != this.dataBase.lastError) { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; } else { statusObject.errorString = e.message; statusObject.errorCode = -1; } yoono.log.error("Unable to start sql transaction : "+e); return [statusObject]; } } var resultSets = []; var rollback = false; var startTime = 0, endTime = 0; if (yoono.prefs.get("debug.db")) { startTime = new Date().getTime(); yoono.log.debug("Executing SQL " + nbDataSet + " times the statement : " + aStatement ); } /* var lowerCaseStatement = aStatement.toLowerCase(); var oneStepStatement = (0 == lowerCaseStatement.indexOf('insert')) || (0 == lowerCaseStatement.indexOf('delete')) || (0 == lowerCaseStatement.indexOf('update')) */ try { for(var idx = 0 ; idx < nbDataSet; idx ++) { var resultSet = []; // Number of data in this dataset = number of placeholders to bind var nbData = aDataSet[idx].length; if (nbData != statement.parameterCount) { yoono.log.error("Warning : this SQL query -> <<"+aStatement+">> does'nt get his placeholders correctly set. "+nbData+" instead of "+statement.parameterCount+"!"); } // set the values for place holders, if any for(var jdx = 0 ; jdx < nbData ; jdx ++) { var aData = aDataSet[idx][jdx]; if(null == aData) { statement.bindNullParameter(jdx); } else { statement.bindUTF8StringParameter(jdx, aData); } } // Execute the statement var notDone = true; do { try { notDone = statement.executeStep(); } catch (e) { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; yoono.log.error("Error on executeStep for SQL query : "+aStatement+"\niteration " + idx + " of " + nbDataSet + " because of : " + statusObject.errorString + " (" + statusObject.errorCode + ")\nor because of : "+e+"\n" + "Data : " + aDataSet[idx].join(' , ')); break; } if(!notDone) break; var result = []; // one line of result var column = null; for(var offset = 0; offset < statement.columnCount; offset++) { column = statement.getUTF8String(offset); result.push(column); } // If no results, do not put an empty array in the results if(statement.columnCount) resultSet.push(result); // if(oneStepStatement) notDone = false; } while(notDone); resultSets.push(resultSet); statement.reset(); } } catch(e) { if(0 != this.dataBase.lastError) { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; } else { statusObject.errorString = e.message; statusObject.errorCode = -1; } yoono.log.error("Unable to execute SQL query : "+aStatement+"\niteration " + idx + " because of : " + this.dataBase.lastErrorString + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n"); yoono.log.error("Data offset " + idx + " on " + nbData + " : " + aDataSet[idx].join(' , ')); } try { statement.finalize(); } catch(e) { yoono.log.error("Unable to execute finalize for statement : "+aStatement+"\nbecause of : " + this.dataBase.lastErrorString + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n"); if(0 != this.dataBase.lastError) { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; } else { statusObject.errorString = e.message; statusObject.errorCode = -1; } } // No transaction to commit or rollback if running a vacuum statement if('vacuum' != aStatement) { try { if(rollback) { this.dataBase.rollbackTransaction(); } else { this.dataBase.commitTransaction(); } statusObject.lastInsertRowID = this.dataBase.lastInsertRowID ; } catch(e) { yoono.log.error("Unable to execute rollback or commit for statement : "+aStatement+"\nbecause of : " + this.dataBase.lastErrorString + " (" + this.dataBase.lastError + ")\nor because of : "+e+"\n"); if(0 != this.dataBase.lastError) { statusObject.errorString = this.dataBase.lastErrorString; statusObject.errorCode = this.dataBase.lastError; } else { statusObject.errorString = e.message; statusObject.errorCode = -1; } } } if (yoono.prefs.get("debug.db")) { endTime = new Date().getTime(); yoono.log.debug("Executing SQL took : "+ (endTime - startTime)+"\n"); } resultSets.unshift(statusObject) return(resultSets); } YoonoStorage.prototype.hasErrorOccured = function() { return(this.dataBase.lastError && (this.dataBase.lastError < 100)); } /** * * @param aStatement * @param aDataset * @param aCallback : object with property 'callback' holding the actuall callback function (that's because of IE) */ YoonoStorage.prototype.executeAsyncSql = function(aStatement, aDataset, aCallback) { var storage=this; storage.backgroundThread.dispatch( { run: function() { var results = storage.executeSql(aStatement,aDataset); storage.mainThread.dispatch({ run: function () { if (aCallback && (typeof aCallback.callback=="function")) aCallback.callback(results); } }, storage.mainThread.DISPATCH_NORMAL); } }, storage.backgroundThread.DISPATCH_NORMAL); } YoonoStorage.prototype.closeStorage = function() { if (!this.dataBase) return; this.dataBase.close(); this.dataBase = null; } /** * Backup the DB before a schema migration. * If backup already exists, it's not overwritten * @param aVersion */ YoonoStorage.prototype.backupDB = function(aVersion) { var dbFile = this.getDBFile(); var path = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile) path.append('yoono'); var newNameElts = this.dbFileName.split('.'); newNameElts[0] += '_' + aVersion + '.'; dbFile.copyTo(path, newNameElts.join('')); } YoonoStorage.prototype.openStorage = function() { if (this.dataBase) return; var dbFile = this.getDBFile(); if(!dbFile.exists()) { this.createAndOpenStorage(dbFile); } else { this.openDBStorage(dbFile); } } YoonoStorage.prototype.getCurrentDBFileName = function() { return(this.dbFileName); } YoonoStorage.prototype.getDBFile = function() { var dbFile = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile) dbFile.append('yoono'); var userCredentials = yoono.main.getUserCredential(); var dbName = 'yoono_friends_db.sqlite'; if(userCredentials) { if(userCredentials.anonymous) { dbName = userCredentials.userId + '_friends_db.sqlite'; } else { dbName = yoono.main.escapeFileName(userCredentials.login) + '_friends_db.sqlite'; } } dbFile.append(dbName); // Fix some weird bug in yoono desktop 6.2 -> 7 migration if (dbFile.exists() && dbFile.isDirectory()) dbFile.remove(true); this.dbFileName = dbName; return(dbFile); } /** * This method closes the database, renames the current file to the new file, and reopens the database */ YoonoStorage.prototype.switchDBStorage = function() { this.closeStorage(); var currentFileName = this.getCurrentDBFileName(); var filePath = YOONO_DIRSERVICE.get("ProfDS", CI.nsIFile) filePath.append('yoono'); var currentFile = filePath.clone(); var newFilePath = filePath.clone(); currentFile.append(currentFileName); var aFile = this.getDBFile(); var newFileName = this.getCurrentDBFileName(); // was updated by getDBFile() newFilePath.append(newFileName); if(newFilePath.exists()) { newFilePath.remove(false); } if(!currentFileName) { this.createAndOpenStorage(newFilePath); } else { currentFile.moveTo(filePath, newFileName); this.openDBStorage(aFile); } } YoonoStorage.prototype.openDBStorage = function(aFile) { var storageService = CL["@mozilla.org/storage/service;1"].getService(CI.mozIStorageService); // Open sqlite DB in unshared mode for FTS3 this.dataBase = storageService.openUnsharedDatabase(aFile); } YoonoStorage.prototype.createAndOpenStorage = function(aFile) { aFile.create(CI.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); this.openDBStorage(aFile); } var YOONO_STORAGE = new YoonoStorage();